Add HttpFilterIntegrationTestBase#517
Conversation
- Extracts shared needs between tested extensions. - Sanitized api, better naming. - Doc comments. - Support for testing POST request methods and entity bodies to check the alternative flow that will trigger in extensions when using that. Prelude to envoyproxy#512, which includes it and provides a means to see the end goal. Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
|
@eric846 please review and assign to me when done. |
…r-prelude Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
| * up a fake upstream to supply the response. | ||
| * | ||
| * @param request_level_config Configuration to be delivered by request header. For example | ||
| * "{{response_body_size:1024}". |
| * extension under test should supply the response. | ||
| * | ||
| * @param request_level_config Configuration to be delivered by request header. For example | ||
| * "{{response_body_size:1024}". |
| } | ||
|
|
||
| Envoy::IntegrationStreamDecoderPtr | ||
| HttpFilterIntegrationTestBase::getResponse(absl::string_view request_level_config, |
There was a problem hiding this comment.
Could we replace this getResponse overload with a helper that translates a string into an Envoy::Http::TestRequestHeaderMapImpl and use the helper inline?
| * @brief Fetches a response with request-level configuration set in the request header. | ||
| * | ||
| * @param request_level_config Configuration to be delivered by request header. For example | ||
| * "{{response_body_size:1024}". |
| getResponse(const Envoy::Http::TestRequestHeaderMapImpl& request_headers, | ||
| bool setup_for_upstream_request); | ||
|
|
||
| const Envoy::Http::TestRequestHeaderMapImpl request_headers_; |
There was a problem hiding this comment.
What about default_request_headers_?
| * be used to inspect the response. | ||
| */ | ||
| Envoy::IntegrationStreamDecoderPtr getResponse(absl::string_view request_level_config, | ||
| bool setup_for_upstream_request); |
There was a problem hiding this comment.
Could we reduce the number of public methods by half and eliminate the need to figure out the meaning of true or false in the implementation by creating a mode enum with values kUpstream and kExtension? Or if it's more convenient for the test authors to have the separate public methods, a private enum could still make the impl easier to read.
| Envoy::IntegrationStreamDecoderPtr | ||
| HttpFilterIntegrationTestBase::getResponse(absl::string_view request_level_config, | ||
| bool setup_for_upstream_request) { | ||
| const Envoy::Http::LowerCaseString key("x-nighthawk-test-server-config"); |
There was a problem hiding this comment.
It would be good to mention in all doc comments that request_level_config is sent as the x-nighthawk-test-server-config header -- I didn't realize how it worked.
| * @return Envoy::IntegrationStreamDecoderPtr Pointer to the integration stream decoder, which can | ||
| * be used to inspect the response. | ||
| */ | ||
| Envoy::IntegrationStreamDecoderPtr getResponseFromUpstream(); |
There was a problem hiding this comment.
Are we missing getResponseFromExtension() with no args, or does that one not make sense?
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
…r-prelude Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
|
Thanks for the review @eric846. Following some of your suggestions, I refactored this some more to further simplify in |
|
LGTM, thanks! |
…r-prelude Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Split out from envoyproxy#512 Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com> ------ Prerequisite: - [ ] Land envoyproxy#517
| * | ||
| * @param config configuration to pass to Envoy::HttpIntegrationTest::config_helper_.addFilter. | ||
| */ | ||
| void setup(const std::string& config); |
There was a problem hiding this comment.
I think having a setup and a SetUp is confusing, especially since it's not really consistent with our capitalization scheme. Can we rename this to something like initializeConfig?
| : HttpIntegrationTest(Envoy::Http::CodecClient::Type::HTTP1, ip_version), | ||
| request_headers_({{":method", "GET"}, {":path", "/"}, {":authority", "host"}}) {} | ||
|
|
||
| void HttpFilterIntegrationTestBase::setup(const std::string& config) { |
There was a problem hiding this comment.
Just to check - this can't be an absl::string_view?
There was a problem hiding this comment.
Done -- this was slightly more convenient in the implementation as we need to copy, but I agree consistency outweighs that. Thanks for catching
| const bool is_post = request_headers_.Method()->value().getStringView() == | ||
| Envoy::Http::Headers::get().MethodValues.Post; | ||
| const uint64_t request_body_size = is_post ? 1024 : 0; | ||
| if (expected_origin == ResponseOrigin::UPSTREAM) { |
There was a problem hiding this comment.
I'm having trouble understanding this function's full intent. There are a lot of conditionals that seem to make the behavior very different. Is this actually common behavior across filters?
Maybe some comments in this section about why things are happening would be helpful.
There was a problem hiding this comment.
I added some comments in ca17f41. Does that provide the type of context you'd consider helpful when reading this code?
Basically, this support call is an extract of the what we need to cover the stream codec code for all our extensions. We'll later see that this allows us deprecate much more complex code (which was the way to do it a while back), as wel as deduplicate some similar code.
There was a problem hiding this comment.
Additionally, for some more concrete context, #533 is the direct follow up to this (currently in draft, as it depends on this PR, and will lean up once we merge this one and merge the result into that). It brings in a new test file which is using this extensively.
That test file adds tests for ensuring basic behavioural guarantees across all of our extensions.
The behaviours we are looking for involve certain configuration (error) handling, as well as handling default/empty configuration, both static from disk as well as dynamically delivered via response headers. These tests are parameterised over GET and POST requests.
Once we land that, we can in turn start purging / enhancing the other test files, and make them more about just testing the functionality that the extension brings in. Where applicable, it will also use the functionality we add in this PR, and we can purge their own variant(s).
There was a problem hiding this comment.
(NOTE: the changes from the last paragraph above, are already staged over at #512)
…r-prelude Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
|
@dubious90 feedback address in ca17f41, thanks for the review! |
dubious90
left a comment
There was a problem hiding this comment.
Thank you for adding all of the comments. I understand what is happening here a lot more now. Have some suggestions on naming and documentation, but as always, open to pushback.
| namespace Nighthawk { | ||
|
|
||
| /** | ||
| * Base class with shared functionality for testing Nighthawk test server http filter extensions. |
There was a problem hiding this comment.
Can we add two notes/clarifications here: 1. This class is stateful. 2. Unless I'm mistaken I don't think this class is threadsafe
| HttpFilterIntegrationTestBase(Envoy::Network::Address::IpVersion ip_version); | ||
|
|
||
| /** | ||
| * We don't override SetUp(): tests using this fixture must call setup() instead. |
There was a problem hiding this comment.
nit: reference to setup() here is no longer accurate
| // This is useful, because emitting an entity body will hit distinct code in extensions. Hence we | ||
| // facilitate that. | ||
| const uint64_t request_body_size = is_post ? 1024 : 0; | ||
| // An extension can either act as an origin and synthesize a reply, or delegate that |
There was a problem hiding this comment.
Looking through again, these comments are very helpful. Thank you for adding them
| absl::string_view header_value); | ||
|
|
||
| /** | ||
| * Fetch a response. The request headers default to a minimal GET, but this may be changed |
There was a problem hiding this comment.
Can we rephrase this comment a little (correct anything here that is inaccurate):
Fetch a response, according to the options specified by the class methods. By default, simulates a GET request with minimal headers.
| * We don't override SetUp(): tests using this fixture must call setup() instead. | ||
| * This is to avoid imposing the need to create a fixture per filter configuration. | ||
| * | ||
| * @param config configuration to pass to Envoy::HttpIntegrationTest::config_helper_.addFilter. |
There was a problem hiding this comment.
is this configuration for the filter being tested? If so, can we clarify "filter configuration"
| /** | ||
| * Indicate the expected response origin. | ||
| */ | ||
| enum class ResponseOrigin { UPSTREAM, EXTENSION }; |
There was a problem hiding this comment.
Can we add a one line summary here for each type of ResponseOrigin? This may also allow you to remove a comment elsewhere, if it's duplication at this point.
| * @param request_level_config Configuration to be delivered by request-header in future calls to | ||
| * getResponse(). For example: "{response_body_size:1024}". | ||
| */ | ||
| void updateRequestLevelConfiguration(absl::string_view request_level_config); |
There was a problem hiding this comment.
nit/optional: How do you feel about changing this to setRequestLevelConfiguration? I think it'd then be more consistent with setRequestHeader
| * Switch getResponse() to use the POST request method with an entity body. | ||
| * Doing so will make tests hit a different code paths in extensions. | ||
| */ | ||
| void switchToPostWithEntityBody(); |
There was a problem hiding this comment.
Just a thought, curious what you think - it might be more self-documenting here, if we take in a parameter for the post body size. Or even then, rename this to setPostBodySize, with an indication that unless this is called, the requests into the filter will be GET requests.
There was a problem hiding this comment.
I would be more up for doing this, but rather do it once a functional need for it arises (which is very possible). Right now, in the context of this PR and the changes coming after this, I'm on a mission to minimize and contain complexity that has organically grown while we added the test server and the two new extensions. Part of effort is making everything go through this hopefully well defined but minimal functionality. It should be trivial to refactor this later on according to what you suggest, once the need arises in new tests?
There was a problem hiding this comment.
That makes sense to me. I'm happy to push it through as is, and if it comes up, do the refactor as you suggest.
…r-prelude Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
|
@dubious90 feedback addressed in dubious90 modulo one thing, I will reply to that specific comment to explain. |
dubious90
left a comment
There was a problem hiding this comment.
LGTM. Thank you for addressing my concerns.
Prelude to #512, which includes it and provides a means to
see the end goal.
to check the alternative flow that will trigger in extensions
when using that.
Signed-off-by: Otto van der Schaaf oschaaf@we-amp.com